/**
 * Copyright 2002 Cordys R&D B.V. 
 * 
 * This file is part of the Cordys .NET Connector. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 *
 * @author  owarnier
 * changed by pkavuri
 */

package com.eibus.applicationconnector.ejb;



import javax.naming.*;
import javax.rmi.PortableRemoteObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.eibus.util.logger.CordysLogger;



class EJBCall {
    private EJBExecutionEnvironment _ejbExec;
    private String _ejbName;
    private Object _activeHomeEJBObject;
    private Object _activeRemoteEJBObject;
    static CordysLogger  ejbLogger = CordysLogger.getCordysLogger(EJBCall.class);
    
    /** Creates a new instance of EJBCall <BR>
     *  When you use this constructor beware that you have to take care of your own classloading<br>
     *  It is recommeded to construct a @see EJBExecutionEnvironment and call the method startCall from that
     *  as it contructs the seperate memory environment including the EJB server support classes for
     *  handling the call
     *  @param the jndi name of the EJB to be called
     */
    EJBCall(String ejbName) {
        _ejbName = ejbName;
    }
    
    /**
     * sets the execution environment for this EJBCall. <BR>
     * @see: EJBExecutionEnvironment for more details.
     * @param the EJB execution environment for this call
     */
    void setExecutionEnvironment(EJBExecutionEnvironment ejbExec) {
        _ejbExec = ejbExec;
    }
    
    /**
     *  Set the JNDI name of the EJB (must be the same as the name of the EJB in the ejb-jar.xml file)
     *  @param the JNDI name of the EJB to be called
     */
    void setEJBName(String ejbName) {
        _ejbName = ejbName;
    }
    
    String getEJBName() {
        return _ejbName;
    }
    
    
    /**
     * Set the way you want the remote interface created.
     *
     * @param the name of the method to create a single object of the type of the RemoteInterface of the EJB object
     * @param a list of parameters that the method on the home interface expects
     * @param a list of parameter types that the method on the home interface expects. <BR>
     * Please note that this should match the parameter list in amount and order of parameters. <BR>
     * When no exception is thrown, the remote object is created according the give parameters.
     */
     void createRemoteObject(String method, Object[] parameters, Class[] parameterTypes) throws EJBCallException {
        EJBObjectMetaData foundMD = null;
        if (ejbLogger.isDebugEnabled())
        	ejbLogger.debug("createRemoteObject() with method:" + method);
        // check the minimal parameters required
        if (_ejbName == null || method == null) {
            if (ejbLogger.isWarningEnabled())
            	ejbLogger.warn("The ejbName or the method is not set");
            throw new EJBCallException("createRemoteObject: the ejbName or method is not set");
        }
        try {
            foundMD = _ejbExec.getMetaData(_ejbName);
        }
        catch (EJBExecutionEnvironmentException eEx) {
            if (ejbLogger.isErrorEnabled())
            	ejbLogger.error("createRemoteObject: could not find the MetaData.",eEx);
            throw new EJBCallException("createRemoteObject: could not find the MetaData:" + eEx.getMessage());
        }
        if (foundMD == null) {
            if (ejbLogger.isErrorEnabled())
            	ejbLogger.error("Could not find the EJBObjectMetaData for " +_ejbName);
            throw new EJBCallException("createRemoteObject: did not find the EJBObjectMetaData");
        }
        try {
            Context jndiContext = _ejbExec.getNamingContext();
            if (ejbLogger.isDebugEnabled()){
            	ejbLogger.debug("Performing Lookup Operation with " + foundMD.getName());
            	ejbLogger.debug("Doing a look up for jnidiName "+foundMD.getEjbJndiName());
            	
            }
            //Object ref   = jndiContext.lookup(foundMD.getName());
            Object ref   = jndiContext.lookup(foundMD.getEjbJndiName());
            if (ejbLogger.isDebugEnabled())
            	ejbLogger.debug("Cast the Home with PortableRemoteObject to " + foundMD.getRemoteHomeName());
            Class refHomeClass = Class.forName(foundMD.getRemoteHomeName());
            _activeHomeEJBObject = PortableRemoteObject.narrow(ref,refHomeClass);
        }
        catch (Exception e) {
        	if (ejbLogger.isErrorEnabled())
        		ejbLogger.error("Lookup or creation of the Home Object failed.",e);
            throw new EJBCallException("createRemoteObject: lookup or creation of the Home Object failed :" + e.getMessage());
        }
        // invoke a method on the home interface to create a remote bean
        try {
            // set the parameters
            if (parameters != null && parameterTypes != null) {
                if (parameters.length != parameterTypes.length) {
                	if (ejbLogger.isWarningEnabled())
                		ejbLogger.warn("Mismatch in the number of ParameterTypes & Parameters.");
                    throw new EJBCallException("Mismatch in the number of ParameterTypes & Parameters.");
                }
            }
            Class refRemoteClass = Class.forName(foundMD.getRemoteName()); // for the time being
            Method createMethod = _activeHomeEJBObject.getClass().getMethod(method, parameterTypes);
            if (ejbLogger.isDebugEnabled())
            	ejbLogger.debug("invoke the " + method + " method on the home interface");
            _activeRemoteEJBObject = createMethod.invoke(_activeHomeEJBObject, parameters);
        }
        catch (NoSuchMethodException e) {
        	if (ejbLogger.isErrorEnabled())
        		ejbLogger.error("createRemoteObject: the " + method + " method on the Home Object is not found" + e.getMessage(),e);
            throw new EJBCallException("createRemoteObject: the " + method + " method on the Home Object is not found");
        }
        catch (IllegalAccessException e) {
            if (ejbLogger.isErrorEnabled())
            	ejbLogger.error("createRemoteObject: it is not allowed to access this " + method + " method" ,e);
            throw new EJBCallException("createRemoteObject: it is not allowed to access this " + method + " method");
        }
        catch (InvocationTargetException e) {
        	if (ejbLogger.isErrorEnabled())
        		ejbLogger.error("createRemoteObject: could not invoke the create " + method ,e);
            throw new EJBCallException("createRemoteObject: could not invoke the " + method + " cause:" + e.getCause().getMessage());
        }
        catch (ClassNotFoundException e) {
        	if (ejbLogger.isErrorEnabled())
        		ejbLogger.error("createRemoteObject: could not find the class:" + foundMD.getRemoteName(),e);
            throw new EJBCallException("createRemoteObject: could not find the class:" + foundMD.getRemoteName());
        }
    } // end of createRemoteObject
    
    /**
     *  Set the method you want to invoke on the remote EJB
     * @param the name of the method to invoke on the RemoteInterface of the EJB object
     * @param a list of parameters that the method on the home interface expects
     * @param a list of parameter types that the method on the home interface expects. <BR>
     * Please note that this should match the parameter list in amount and order of parameters.
     * @return the return value of the method called on the EJB as an Object
     */
     Object invokeRemote(String method, Object[] parameters, Class[] parameterTypes) throws EJBCallException {
       
     	if (parameters != null && parameterTypes != null) {
            if (parameters.length != parameterTypes.length) {
            	if (ejbLogger.isWarningEnabled())
            		ejbLogger.warn("Mismatch in the number of ParameterTypes & Parameters.");
                
                throw new EJBCallException("invokeRemote: Mismatch in the number of ParameterTypes & Parameters.");
            }
        }
        if (_activeRemoteEJBObject == null) {
        	if (ejbLogger.isWarningEnabled())
        		ejbLogger.warn("There is no active Remote Object, please call createRemoteMethod first");
            throw new EJBCallException("invokeRemote: there is no active Remote Object, please call createRemoteObject first");
        }
        Object retObject = null;
        try {
            Method invokeMethod = _activeRemoteEJBObject.getClass().getMethod(method, parameterTypes);
            retObject = invokeMethod.invoke(_activeRemoteEJBObject,parameters);
        }
        catch (NoSuchMethodException e) {
            if (ejbLogger.isWarningEnabled())
            	ejbLogger.warn("The " + method + " method is not found.",e);
            throw new EJBCallException("invokeRemote: the " + method + " method is not found");
        }
        catch (IllegalAccessException e) {
        	if (ejbLogger.isWarningEnabled())
        		ejbLogger.warn("It is not allowed to access this " + method + " method." ,e);
            throw new EJBCallException("invokeRemote: it is not allowed to access this " + method + " method");
        }
        catch (InvocationTargetException e) {
        	if (ejbLogger.isWarningEnabled())
        		ejbLogger.warn("Could not invoke the " + method + " method." ,e);
            throw new EJBCallException("could not invoke the " + method + " method cause:" + e.getCause().getMessage());
        }
        return retObject;
    } // end invokeRemote(...)
}
